package cn.easyutil.veteran.web.session;

import cn.easyutil.veteran.core.entity.SessionUser;
import cn.easyutil.veteran.core.error.CommonException;
import cn.easyutil.veteran.core.error.ErrorEnum;
import cn.easyutil.veteran.core.logger.LoggerUtil;
import cn.easyutil.veteran.core.utils.SpringUtils;
import cn.easyutil.veteran.web.VeteranWebConfiguration;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

public class SessionOperation {

    //缓存中的用户信息
    public static final ThreadLocal<SessionUser> user = new ThreadLocal<>();
    //当前请求的参数信息
    public static final ThreadLocal<Object> requestParams = new ThreadLocal<>();
    //当前请求相应的参数
    public static final ThreadLocal<Object> responseParams = new ThreadLocal<>();
    //当前请求的响应数据是否需要被包装
    public static final ThreadLocal<Boolean> toPackage = new ThreadLocal<>();
    //当前请求的参数和响应数据是否需要进行AES加解密
    public static final ThreadLocal<Boolean> toSecret = new ThreadLocal<>();
    //当前请求的接口是否需要判断登陆权限
    public static final ThreadLocal<Boolean> toAuth = new ThreadLocal<>();
    //当前请求是否打印日志
    public static final ThreadLocal<Boolean> toLog = new ThreadLocal<>();
    //当前请求如果报错是否返回堆栈信息
    public static final ThreadLocal<Boolean> toTrance = new ThreadLocal<>();
    //当前请求的日志流水记录
    public static final ThreadLocal<List<SessionLogStorage>> logs = new ThreadLocal<>();
    //web配置信息
    private static VeteranWebConfiguration configuration;
    //用户获取、校验和刷新缓存的实现
    private static SessionUserFetch<? extends SessionUser> sessionUserFetch;
    //token的值
    public static final ThreadLocal<String> tokenValue = new ThreadLocal<>();

    /**
     * 获取当前登陆用户(不报错)
     */
    public static <T extends SessionUser>T getUserNotException(){
        SessionUser currentUser = user.get();
        if(currentUser != null){
            return (T) currentUser;
        }
        if(getConfiguration().isUseDefaultUser()){
            currentUser = new SessionUser();
            currentUser.setUserId(getConfiguration().getDefaultUserId());
            currentUser.setAccount(getConfiguration().getDefaultUserAccount());
            currentUser.setApiPaths(new HashSet<>(Collections.singleton("/**")));
            currentUser.setSecret(getConfiguration().getDefaultUserSecret());
            user.set(currentUser);
            return (T) currentUser;
        }
        //从缓存中获取
        currentUser = getSessionUserFetch().getUser(tokenValue.get());
        user.set(currentUser);
        return (T) currentUser;
    }

    /**
     * 获取当前登陆用户，取不到抛出未登录异常
     */
    public static <T extends SessionUser>T getUser(){
        SessionUser user = getUserNotException();
        if(user == null){
            throw new CommonException(ErrorEnum.user_not_login);
        }
        return (T) user;
    }

    /**
     * 设置缓存用户信息
     */
    public static <T extends SessionUser> void setUser(T user){
        getSessionUserFetch().setUser(user);
        SessionOperation.user.set(user);
    }

    /**
     * 修改缓存用户信息
     */
    public static <T extends SessionUser> void updateUser(T user){
        getSessionUserFetch().updateUser(user);
        SessionOperation.user.set(user);
    }

    /**
     * 刷新用户缓存
     */
    public static void refresh(){
        if(getConfiguration().isUseDefaultUser()){
            return ;
        }
        getSessionUserFetch().refresh(tokenValue.get());
    }

    /**
     * 清除用户登陆信息
     */
    public static void clear(){
        user.remove();
        toAuth.remove();
        toSecret.remove();
        toPackage.remove();
        toLog.remove();
        tokenValue.remove();
        requestParams.remove();
        LoggerUtil.clearTransId();
        responseParams.remove();
        toTrance.remove();
        logs.remove();
    }

    /**
     * 用户请求进入
     */
    public static void request(){
        HttpServletRequest request = getRequest();
        String requestURI = request.getRequestURI();
        PathMatcher matcher = new AntPathMatcher();
        //判断是否需要日志打印
        toLog.set(false);
        boolean excludeLog = getConfiguration().getExcludeLogUri().stream().anyMatch(uri -> matcher.match(uri, requestURI));
        if(!excludeLog){
            toLog.set(getConfiguration().getIncludeLogUri().stream().anyMatch(uri -> matcher.match(uri, requestURI)));
        }
        //判断是否需要进行包装
        toPackage.set(false);
        boolean excludePackage = getConfiguration().getExcludePackageUri().stream().anyMatch(uri -> matcher.match(uri, requestURI));
        if(!excludePackage){
            toPackage.set(getConfiguration().getIncludePackageUri().stream().anyMatch(uri -> matcher.match(uri, requestURI)));
        }
        //判断是否需要登陆权限
        toAuth.set(false);
        boolean excludeAuth = getConfiguration().getExcludeAuthUri().stream().anyMatch(uri -> matcher.match(uri, requestURI));
        if(!excludeAuth){
            toAuth.set(getConfiguration().getIncludeAuthUri().stream().anyMatch(uri -> matcher.match(uri, requestURI)));
        }
        //判断是否需要进行加解密
        toSecret.set(false);
        boolean excludeSecret = getConfiguration().getExcludeSecretUri().stream().anyMatch(uri -> matcher.match(uri, requestURI));
        if(!excludeSecret){
            toSecret.set(getConfiguration().getIncludeSecretUri().stream().anyMatch(uri -> matcher.match(uri, requestURI)));
            if(toSecret.get() && !toAuth.get()){
                //如果需要加密，则判断当前是否要授权，如果不授权，也同样设置为不加密
                toSecret.set(false);
            }
        }
        tokenValue.set(getHeaderToken());
        toTrance.set(configuration.isEnableErrorTrace());
        LoggerUtil.clearTransId();
    }

    /**
     * 获取用户传递在header头中的token值
     */
    public static String getHeaderToken(){
        return getRequest().getHeader(configuration.getTokenName());
    }

    public static HttpServletRequest getRequest(){
        return ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
    }

    public static HttpServletResponse getResponse(){
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
    }

    public static VeteranWebConfiguration getConfiguration() {
        if(configuration != null){
            return configuration;
        }
        synchronized (SessionOperation.class){
            if(configuration != null){
                return configuration;
            }
            configuration = SpringUtils.getBean(VeteranWebConfiguration.class);
        }
        return configuration;
    }

    public static SessionUserFetch getSessionUserFetch() {
        if(sessionUserFetch != null){
            return sessionUserFetch;
        }
        synchronized (SessionOperation.class){
            if(sessionUserFetch != null){
                return sessionUserFetch;
            }
            sessionUserFetch = SpringUtils.getBean(SessionUserFetch.class);
        }
        return sessionUserFetch;
    }
}
